package org.amphiprion.rfreplayanalyser.ui.lapanalyzer;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.amphiprion.rfreplayanalyser.parser.Driver;
import org.amphiprion.rfreplayanalyser.parser.Replay;
import org.amphiprion.rfreplayanalyser.ui.lapanalyzer.LapDrawerPanel.DisplayMode;
import org.amphiprion.rfreplayanalyser.ui.lapanalyzer.LapDrawerPanel.MouseMode;
import org.amphiprion.rfreplayanalyser.ui.lapanalyzer.LapStartEndAnalyerPanel.AnalyserMode;

import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

@SuppressWarnings("serial")
public class LapAnalyzerFrame extends JFrame implements LapProvider {
	private LapItem selectedLapItem;
	private Replay replay;
	private JPanel contentPane;
	private JPanel pnLapList = new JPanel();
	private LapDrawerPanel drawerPanel = new LapDrawerPanel(this);
	private LapStartEndAnalyerPanel lapStartEndAnalyzerPanel = new LapStartEndAnalyerPanel(this);
	private JScrollPane scrollPane = new JScrollPane();
	private List<LapItem> lapItems = new ArrayList<LapItem>();

	private JComboBox cbMode = new JComboBox();
	private JToolBar toolBar = new JToolBar();
	private JScrollPane spDrawer = new JScrollPane();

	private Point selectedCarScreenPosition;

	private AnalyserMode analyserMode = AnalyserMode.SPEED;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				try {
					LapAnalyzerFrame frame = new LapAnalyzerFrame(null);
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public LapAnalyzerFrame(final Replay replay) {
		setTitle("Lap analyzer");
		this.replay = replay;
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(100, 100, 1061, 735);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new BorderLayout(0, 0));

		JSplitPane splitPane = new JSplitPane();
		contentPane.add(splitPane, BorderLayout.CENTER);

		JPanel panel = new JPanel();
		splitPane.setRightComponent(panel);
		panel.setLayout(new BorderLayout(0, 0));

		panel.add(scrollPane, BorderLayout.CENTER);

		scrollPane.setViewportView(pnLapList);
		pnLapList.setLayout(new GridLayout(100, 1, 0, 5));

		JPanel pnLapToolbar = new JPanel();
		panel.add(pnLapToolbar, BorderLayout.NORTH);

		JLabel lblLaps = new JLabel("Laps :");
		pnLapToolbar.add(lblLaps);

		JButton button = new JButton("+");
		button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				addLap();

			}
		});
		pnLapToolbar.add(button);

		JPanel panelLeft = new JPanel();
		panelLeft.setLayout(new BorderLayout(0, 0));
		splitPane.setLeftComponent(panelLeft);

		spDrawer.setViewportView(drawerPanel);

		JSplitPane splitPane_1 = new JSplitPane();
		splitPane_1.setOrientation(JSplitPane.VERTICAL_SPLIT);
		splitPane_1.setRightComponent(spDrawer);
		panelLeft.add(splitPane_1, BorderLayout.CENTER);
		splitPane_1.setDividerLocation(130);

		JPanel panel_3 = new JPanel();
		panel_3.setLayout(new BorderLayout(0, 0));
		splitPane_1.setLeftComponent(panel_3);

		JPanel panel_4 = new JPanel();
		panel_3.add(panel_4, BorderLayout.EAST);
		panel_4.setLayout(new FormLayout(new ColumnSpec[] { FormFactory.DEFAULT_COLSPEC, }, new RowSpec[] { FormFactory.DEFAULT_ROWSPEC, FormFactory.RELATED_GAP_ROWSPEC, FormFactory.DEFAULT_ROWSPEC, FormFactory.RELATED_GAP_ROWSPEC, FormFactory.DEFAULT_ROWSPEC, FormFactory.RELATED_GAP_ROWSPEC,
				FormFactory.DEFAULT_ROWSPEC, }));

		final JToggleButton tglbtnV = new JToggleButton("V");
		tglbtnV.setToolTipText("Speed");
		tglbtnV.setSelected(true);
		tglbtnV.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (tglbtnV.isSelected()) {
					updateAnalyzerMode(AnalyserMode.SPEED);
				}
			}
		});
		panel_4.add(tglbtnV, "1, 1");

		final JToggleButton tglbtnG = new JToggleButton("G");
		tglbtnG.setToolTipText("Gear");
		tglbtnG.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (tglbtnG.isSelected()) {
					updateAnalyzerMode(AnalyserMode.GEAR);
				}
			}
		});
		panel_4.add(tglbtnG, "1, 3");
		panel_3.add(lapStartEndAnalyzerPanel, BorderLayout.CENTER);
		splitPane.setDividerLocation(700);

		JPanel panel_1 = new JPanel();
		contentPane.add(panel_1, BorderLayout.SOUTH);
		panel_1.setLayout(new BorderLayout(0, 0));

		JPanel panel_2 = new JPanel();
		panel_1.add(panel_2, BorderLayout.WEST);
		panel_2.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

		JLabel lblNewLabel = new JLabel("Mouse : ");
		panel_2.add(lblNewLabel);

		panel_2.add(cbMode);

		toolBar.setFloatable(false);
		panel_1.add(toolBar, BorderLayout.CENTER);

		ButtonGroup group = new ButtonGroup();
		group.add(tglbtnV);
		group.add(tglbtnG);

		final JToggleButton tglbtnT = new JToggleButton("T");
		tglbtnT.setToolTipText("Throttle");
		panel_4.add(tglbtnT, "1, 5");
		group.add(tglbtnT);

		final JToggleButton tglbtnY = new JToggleButton("Y");
		tglbtnY.setToolTipText("Steer Yaw");
		panel_4.add(tglbtnY, "1, 7");
		tglbtnT.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (tglbtnT.isSelected()) {
					updateAnalyzerMode(AnalyserMode.THROTTLE);
				}
			}
		});
		tglbtnY.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (tglbtnY.isSelected()) {
					updateAnalyzerMode(AnalyserMode.STEER_YAW);
				}
			}
		});
		group.add(tglbtnY);

	}

	@Override
	public AnalyserMode getAnalyzerMode() {
		return analyserMode;
	}

	private void updateAnalyzerMode(AnalyserMode mode) {
		analyserMode = mode;
		lapStartEndAnalyzerPanel.revalidate();
		lapStartEndAnalyzerPanel.repaint();
	}

	private void addLap() {
		List<LapItem> newlapItems = LapPickerDialog.show(this, replay);
		if (newlapItems != null) {
			for (LapItem lapItem : newlapItems) {
				lapItems.add(lapItem);
				LapPanel lp = new LapPanel(LapAnalyzerFrame.this, lapItem);
				pnLapList.add(lp);
			}
			updateAnimationRange();
			drawerPanel.recomputeAnalyzerPoints();
			scrollPane.revalidate();
			scrollPane.repaint();
			drawerPanel.revalidate();
			drawerPanel.repaint();

		}
	}

	private void updateMode() {
		if (cbMode.getSelectedIndex() == 0) { //
			drawerPanel.setMouseMode(MouseMode.SELECT);
		} else if (cbMode.getSelectedIndex() == 1) { //
			drawerPanel.setMouseMode(MouseMode.ZOOM);
		} else if (cbMode.getSelectedIndex() == 2) { //
			drawerPanel.setMouseMode(MouseMode.START_ANALYZER_POINT);
		} else if (cbMode.getSelectedIndex() == 3) { //
			drawerPanel.setMouseMode(MouseMode.END_ANALYZER_POINT);
		}
	}

	private void updateAnimationRange() {
		toolBar.removeAll();
		int maxLaptimeInMilli = 0;
		for (LapItem item : lapItems) {
			if (item.laptime * 1000 > maxLaptimeInMilli) {
				maxLaptimeInMilli = (int) (item.laptime * 1000);
			}
		}
		final JSlider timeframe = new JSlider(0, maxLaptimeInMilli);
		timeframe.setValue(0);
		timeframe.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent arg0) {
				drawerPanel.setLaptimeElapsed(timeframe.getValue() / 1000f);
				updateScrollbarPosition();
			}
		});
		toolBar.add(timeframe);
		toolBar.revalidate();
		toolBar.repaint();
	}

	private void updateScrollbarPosition() {
		if (selectedLapItem != null && selectedCarScreenPosition != null) {
			updateScrollbarPositionToCenterAt(selectedCarScreenPosition.x, selectedCarScreenPosition.y);

		}
	}

	@Override
	public void updateScrollbarPositionToCenterAt(int x, int y) {
		spDrawer.getViewport().setViewPosition(new Point(x - spDrawer.getViewport().getWidth() / 2, y - spDrawer.getViewport().getHeight() / 2));

	}

	public static void show(Replay replay) {
		final LapAnalyzerFrame frm = new LapAnalyzerFrame(replay);
		frm.cbMode.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent arg0) {
				frm.updateMode();
			}
		});
		frm.cbMode.addItem("Select");
		frm.cbMode.addItem("Zoom");
		frm.cbMode.addItem("Define Start point");
		frm.cbMode.addItem("Define End point");

		frm.drawerPanel.setDisplayMode(DisplayMode.ANIMATION);

		frm.setVisible(true);
	}

	@Override
	public Replay getReplay() {
		return replay;
	}

	@Override
	public String getDriverName(long driverId) {
		for (Driver d : replay.getDrivers()) {
			if (d.getDriverId() == driverId) {
				return d.getName();
			}
		}
		return "!UNKNOWN!";
	}

	@Override
	public List<LapItem> getLapItems() {
		return lapItems;
	}

	@Override
	public void lapItemUpdated(LapItem lapItem) {
		updateAnimationRange();
		drawerPanel.recomputeAnalyzerPoints();
		scrollPane.revalidate();
		scrollPane.repaint();
		drawerPanel.revalidate();
		drawerPanel.repaint();
	}

	@Override
	public void lapItemRemoved(LapItem lapItem) {
		lapItems.remove(lapItem);
		updateAnimationRange();
		drawerPanel.recomputeAnalyzerPoints();
		scrollPane.revalidate();
		scrollPane.repaint();
		drawerPanel.revalidate();
		drawerPanel.repaint();

	}

	@Override
	public void analyzerRangeUpdated(float[] startAnalyzerTime, float[] endAnalyzerTime) {
		lapStartEndAnalyzerPanel.analyzerRangeUpdated(startAnalyzerTime, endAnalyzerTime);
		lapStartEndAnalyzerPanel.revalidate();
		lapStartEndAnalyzerPanel.repaint();
	}

	@Override
	public void updateReplayTimeForCars(float[] replayTimeAtCarPosition) {
		lapStartEndAnalyzerPanel.updateReplayTimeForCars(replayTimeAtCarPosition);
		lapStartEndAnalyzerPanel.revalidate();
		lapStartEndAnalyzerPanel.repaint();
	}

	@Override
	public void lapItemSelected(LapItem lapItem, boolean centerView) {
		selectedLapItem = lapItem;
		if (lapItem != null) {
			int oldIndex = lapItems.indexOf(lapItem);
			lapItems.remove(oldIndex);
			lapItems.add(lapItem);
			drawerPanel.recomputeAnalyzerPoints();
		}
		scrollPane.revalidate();
		scrollPane.repaint();
		drawerPanel.revalidate();
		drawerPanel.repaint();
		if (centerView) {
			updateScrollbarPosition();
		}

	}

	@Override
	public boolean isLapItemSelected(LapItem lapItem) {
		return lapItem == selectedLapItem;

	}

	@Override
	public void updateScreenPositionSelectedCar(Point selectedCarScreenPosition) {
		this.selectedCarScreenPosition = selectedCarScreenPosition;
	}
}
